using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using Impact.Model.Objects;
using Impact.Controllers;
using Impact.Web.Foundation;
using System.Collections.Generic;
using Impact.Infrastructure.State;
using Impact.Views;
using System.Text.RegularExpressions;
using Impact.Model.Service;
using StructureMap;

namespace Impact.Web.Admin
{
    /// <summary>
    /// code-behind for article editing page
    /// </summary>
    public partial class EditArticle : PageBase, IArticleEditorView, ICallbackEventHandler
    {
        private ArticleController controller;

        /// <summary>
        /// override for OnLoad to perform startup functionality
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad( EventArgs e )
        {

            controller = new ArticleController( this );
            controller.Initialize( !IsPostBack );

            base.OnLoad( e );


            if ( !IsPostBack )
            {
                _title.Focus();
            }


        }

        /// <summary>
        /// processes data binding for this page
        /// </summary>
        protected override void Bind()
        {
            base.Bind();

            // fill the category drop down before populating
            // (base.OnLoad() is calls Bind(), which is executed before
            // the controller calls PopulateForm() in Initialize()
            this._category.DataSource = ArticleController.GetAllCategories( false );
            this._category.DataTextField = ArticleCategory.Columns.Name;
            this._category.DataValueField = ArticleCategory.Columns.CategoryId;
            this._category.DataBind();

            this.tags.DataSource = ArticleController.GetAllTags();
            this.tags.DataBind();
        }


        #region IArticleEditorView Members

        /// <summary>
        /// populate the fields on the form with the Article object
        /// </summary>
        public void PopulateForm()
        {
            if ( !IsPostBack )
            {
                // if not a postback, then clear any article out of cache
                ClearArticle();
            }

            Article article = this.Article;

            this._articleId.Value = article.ArticleId.ToString();
            this._title.Text = article.Title;
            this._body.Value = article.Body;
            this._category.SelectedValue = article.CategoryId.ToString();

        }

        /// <summary>
        /// read the data concerning the article off of the form
        /// </summary>
        /// <returns>an article object filled with the form data</returns>
        public Article GetArticleData()
        {
            Article article = this.Article;

            article.Title = this._title.Text;
            article.Body = this._body.Value;
            article.CategoryId = Int32.Parse( this._category.SelectedValue );

            return article;
        }

        /// <summary>
        /// retrieve a list of tags the user has typed onto the form
        /// </summary>
        public List<string> GetTags()
        {
            List<string> output = new List<string>();
            // add any new tags the user typed in
            if ( newTags.Text.Length > 0 )
            {
                string[] tags = newTags.Text.Split( new char[] { ',', ';' },
                    StringSplitOptions.RemoveEmptyEntries );
                foreach ( string newTag in tags )
                {
                    output.Add( newTag.Trim() );
                }
            }
            return output;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Event handler fired when the user clicks submit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void SubmitCommand( object sender, EventArgs e )
        {
            if ( Page.IsValid )
            {
                controller.SaveArticle();
                ClearArticle();
                Return();
            }
        }

        /// <summary>
        /// Event handler fired when the user clicks cancel
        /// </summary>
        protected void CancelCommand( object sender, EventArgs e )
        {
            ClearArticle();
            Return();
        }

        /// <summary>
        /// event handler fired when the tags repeater is binding items
        /// </summary>
        protected void TagBinding( object sender, RepeaterItemEventArgs e )
        {
            Tag tag = (Tag)e.Item.DataItem;
            HyperLink tagLink = (HyperLink)e.Item.FindControl( "tagLink" );
            tagLink.NavigateUrl = string.Format( "javascript: tagClick('{0}', '{1}');", tag.TagId, tagLink.ClientID );

            if ( Article != null && Article.Tags != null &&
                Article.Tags.GetList().Exists( delegate( ArticleTag target ) { return target.Text == tag.Text; } ) )
            {
                tagLink.CssClass = "TagSelected";
            }
            else
            {
                tagLink.CssClass = "TagUnselected";
            }
        }

        /// <summary>
        /// validate the tags the user has typed in
        /// </summary>
        /// <remarks>
        /// current invalid characters: \, /, #, %, &amp;, ?
        /// </remarks>
        protected void ValidateTags( object source, ServerValidateEventArgs args )
        {
            List<string> tags = GetTags();
            // match reserved characters
            Regex regex = new Regex( "\\\\|[#/%&?]" );
            args.IsValid = tags.TrueForAll( delegate( string s )
            {
                return !regex.IsMatch( s );
            } );
        }



        #endregion

        #region ICallbackEventHandler Members

        /// <summary>
        /// Gets the javascript to use on the client for when a user clicks a tag
        /// </summary>
        protected string TagClickScript
        {
            get
            {
                return this.ClientScript.GetCallbackEventReference( this, "arguments", "tagClickResult", string.Empty );
            }
        }

        /// <summary>
        /// retrieves the results of the callback to return to the client
        /// </summary>
        public string GetCallbackResult()
        {
            string output = callbackReturn;
            callbackReturn = null;
            return output;
        }

        /// <summary>
        /// this method is fired when a asynchronous callback is fired from the client
        /// </summary>
        /// <param name="eventArgument"></param>
        public void RaiseCallbackEvent( string eventArgument )
        {
            string[] args = eventArgument.Split( ',' );

            int tagId = -1;
            if ( Int32.TryParse( args[0], out tagId ) )
            {
                ArticleTagCollection currentTags = Article.Tags;

                ArticleTag tag = currentTags.GetList().Find( delegate( ArticleTag target )
                {
                    return target.TagId.Equals( tagId );
                } );

                bool result;
                if ( tag != null )
                {
                    currentTags.Remove( tag );
                    result = false;
                }
                else
                {
                    ITagService tagService = ObjectFactory.GetInstance<ITagService>();
                    Tag simpleTag = tagService.FetchByID( tagId );

                    tag = new ArticleTag();
                    tag.TagId = simpleTag.TagId;
                    tag.ArticleId = Article.ArticleId;
                    tag.Text = simpleTag.Text;
                    tag.IsNew = true;

                    currentTags.Add( tag );
                    result = true;
                }

                SetArticle();

                // return the control so the client can modify it's appearance
                callbackReturn = string.Format( "{0},{1}", args[1], result.ToString().ToLower() );
            }
        }
        private static string callbackReturn;

        #endregion

        #region Article Edit State

        private string ArticleKey
        {
            get { return string.Format( "{0}/{1}", Transaction.Current.SessionId, "article" ); }
        }

        /// <summary>
        /// retrieves the article that is currently being edited.
        /// this is stored in the cache because when the user clicks the 'tag' 
        /// buttons on the UI, the page does an async postback, and needs to load
        /// the article out of cache to update it.
        /// </summary>
        protected Article Article
        {
            get
            {
                if ( _article == null )
                {
                    //_article = (Article)Cache[ArticleKey];
                    GetArticle();

                    if ( _article == null )
                    {
                        string articleIdParam = Request.QueryString["aid"];
                        int articleId = -1;
                        if ( articleIdParam != null && Int32.TryParse( articleIdParam, out articleId ) )
                        {
                            _article = controller.GetArticle( articleId );
                        }
                        else
                        {
                            _article = new Article();
                            string categoryId = Request.QueryString["cid"];
                            if ( !string.IsNullOrEmpty( categoryId ) )
                            {
                                _article.CategoryId = Int32.Parse( categoryId );
                            }
                        }
                    }

                }
                SetArticle();
                return _article;
            }
        }
        private void GetArticle()
        {
            _article = (Article)Session["article"];
        }
        private void SetArticle()
        {
            //Cache[ArticleKey] = _article;
            Session["article"] = _article;
        }
        private void ClearArticle()
        {
            //Cache.Remove(ArticleKey);
            Session.Remove( "article" );
        }
        private Article _article;

        #endregion

    }
}
